"""
Flask 中间件示例集合
包含常用的中间件实现：认证、日志、错误处理、CORS等
"""

from functools import wraps
from flask import request, jsonify, g, current_app
import time
import logging
from datetime import datetime
from typing import Optional, Callable, Any, Union, Dict
from pydantic import ValidationError


class RequestLoggingMiddleware:
    """请求日志中间件"""
    
    def __init__(self, app=None):
        self.app = app
        if app is not None:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化中间件"""
        app.before_request(self.before_request)
        app.after_request(self.after_request)
        app.teardown_appcontext(self.teardown)
    
    def before_request(self):
        """请求前执行"""
        g.start_time = time.time()
        g.request_id = self.generate_request_id()
        
        # 记录请求信息
        logging.info(
            f"[{g.request_id}] {request.method} {request.url} "
            f"- IP: {request.remote_addr} "
            f"- User-Agent: {request.headers.get('User-Agent', 'Unknown')}"
        )
        
        # 记录请求体（仅开发环境）
        if current_app.debug and request.is_json:
            logging.debug(f"[{g.request_id}] Request Body: {request.get_json()}")
    
    def after_request(self, response):
        """请求后执行"""
        if hasattr(g, 'start_time'):
            duration = time.time() - g.start_time
            logging.info(
                f"[{g.request_id}] Response: {response.status_code} "
                f"- Duration: {duration:.3f}s"
            )
        return response
    
    def teardown(self, exception):
        """请求结束时执行"""
        if exception:
            logging.error(f"[{g.request_id}] Exception: {str(exception)}")
    
    @staticmethod
    def generate_request_id() -> str:
        """生成请求ID"""
        import uuid
        return str(uuid.uuid4())[:8]


class AuthenticationMiddleware:
    """认证中间件"""
    
    def __init__(self, app=None, secret_key: str = None):
        self.secret_key = secret_key
        self.app = app
        if app is not None:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化认证中间件"""
        self.secret_key = self.secret_key or app.config.get('SECRET_KEY')
        if not self.secret_key:
            raise ValueError("SECRET_KEY is required for authentication")
    
    def require_auth(self, f):
        """认证装饰器"""
        @wraps(f)
        def decorated(*args, **kwargs):
            token = self.get_token_from_request()
            
            if not token:
                return jsonify({'message': '缺少认证令牌'}), 401
            
            try:
                payload = self.decode_token(token)
                g.current_user_id = payload.get('user_id')
                g.current_user = payload.get('user_info', {})
                
            except jwt.ExpiredSignatureError:
                return jsonify({'message': '令牌已过期'}), 401
            except jwt.InvalidTokenError as e:
                return jsonify({'message': f'无效的令牌: {str(e)}'}), 401
            except Exception as e:
                logging.error(f"认证错误: {str(e)}")
                return jsonify({'message': '认证失败'}), 401
            
            return f(*args, **kwargs)
        return decorated
    
    def optional_auth(self, f):
        """可选认证装饰器"""
        @wraps(f)
        def decorated(*args, **kwargs):
            token = self.get_token_from_request()
            
            if token:
                try:
                    payload = self.decode_token(token)
                    g.current_user_id = payload.get('user_id')
                    g.current_user = payload.get('user_info', {})
                except:
                    # 可选认证失败时不阻止访问
                    g.current_user_id = None
                    g.current_user = None
            else:
                g.current_user_id = None
                g.current_user = None
            
            return f(*args, **kwargs)
        return decorated
    
    def get_token_from_request(self) -> Optional[str]:
        """从请求中获取令牌"""
        # 从 Authorization 头获取
        auth_header = request.headers.get('Authorization')
        if auth_header and auth_header.startswith('Bearer '):
            return auth_header[7:]
        
        # 从查询参数获取
        token = request.args.get('token')
        if token:
            return token
        
        return None
    
    def decode_token(self, token: str) -> dict:
        """解码JWT令牌"""
        return jwt.decode(token, self.secret_key, algorithms=['HS256'])
    
    def generate_token(self, user_id: int, user_info: dict = None, 
                      expires_in: int = 3600) -> str:
        """生成JWT令牌"""
        payload = {
            'user_id': user_id,
            'user_info': user_info or {},
            'exp': datetime.utcnow().timestamp() + expires_in,
            'iat': datetime.utcnow().timestamp()
        }
        return jwt.encode(payload, self.secret_key, algorithm='HS256')


class ValidationMiddleware:
    """数据验证中间件"""
    
    @staticmethod
    def validate_json(model_class):
        """JSON数据验证装饰器"""
        def decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                try:
                    # 获取JSON数据
                    data = request.get_json()
                    
                    if data is None:
                        return jsonify({
                            'success': False,
                            'message': '请求数据不能为空或格式错误',
                            'timestamp': datetime.now().isoformat()
                        }), 400
                    
                    # 使用Pydantic验证
                    validated_data = model_class(**data)
                    
                    # 将验证后的数据添加到请求对象
                    request.validated_data = validated_data
                    
                    return f(*args, **kwargs)
                    
                except ValidationError as e:
                    return jsonify({
                        'success': False,
                        'message': '数据验证失败',
                        'errors': ValidationMiddleware.format_validation_errors(e.errors()),
                        'timestamp': datetime.now().isoformat()
                    }), 400
                    
                except Exception as e:
                    logging.error(f"数据验证中间件错误: {str(e)}")
                    return jsonify({
                        'success': False,
                        'message': '数据处理失败',
                        'timestamp': datetime.now().isoformat()
                    }), 400
            
            return decorated_function
        return decorator
    
    @staticmethod
    def validate_query(model_class):
        """查询参数验证装饰器"""
        def decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                try:
                    # 获取查询参数
                    query_params = request.args.to_dict()
                    
                    # 使用Pydantic验证
                    validated_query = model_class(**query_params)
                    
                    # 将验证后的查询参数添加到请求对象
                    request.validated_query = validated_query
                    
                    return f(*args, **kwargs)
                    
                except ValidationError as e:
                    return jsonify({
                        'success': False,
                        'message': '查询参数验证失败',
                        'errors': ValidationMiddleware.format_validation_errors(e.errors()),
                        'timestamp': datetime.now().isoformat()
                    }), 400
                    
                except Exception as e:
                    logging.error(f"查询参数验证错误: {str(e)}")
                    return jsonify({
                        'success': False,
                        'message': '查询参数处理失败',
                        'timestamp': datetime.now().isoformat()
                    }), 400
            
            return decorated_function
        return decorator
    
    @staticmethod
    def format_validation_errors(pydantic_errors: list) -> dict:
        """格式化Pydantic验证错误"""
        formatted_errors = {}
        
        for error in pydantic_errors:
            field_path = ".".join(str(x) for x in error["loc"])
            formatted_errors[field_path] = {
                "message": error["msg"],
                "type": error["type"],
                "input": error.get("input")
            }
        
        return formatted_errors


class RateLimitMiddleware:
    """请求限制中间件"""
    
    def __init__(self, app=None):
        self.app = app
        self.requests = {}  # 简单内存存储，生产环境建议使用Redis
        
    def init_app(self, app):
        """初始化限流中间件"""
        app.before_request(self.check_rate_limit)
    
    def rate_limit(self, max_requests: int = 60, window: int = 60, 
                   key_func: Callable = None):
        """限流装饰器"""
        def decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                # 获取限流键
                if key_func:
                    key = key_func()
                else:
                    key = request.remote_addr
                
                # 检查限流
                if self.is_rate_limited(key, max_requests, window):
                    return jsonify({
                        'message': f'请求过于频繁，请在 {window} 秒后重试',
                        'retry_after': window
                    }), 429
                
                return f(*args, **kwargs)
            return decorated_function
        return decorator
    
    def check_rate_limit(self):
        """全局请求前检查"""
        # 清理过期记录
        self.cleanup_expired_records()
    
    def is_rate_limited(self, key: str, max_requests: int, window: int) -> bool:
        """检查是否触发限流"""
        now = time.time()
        window_start = now - window
        
        # 获取或创建记录
        if key not in self.requests:
            self.requests[key] = []
        
        # 移除窗口外的请求记录
        self.requests[key] = [
            req_time for req_time in self.requests[key] 
            if req_time > window_start
        ]
        
        # 检查是否超过限制
        if len(self.requests[key]) >= max_requests:
            return True
        
        # 记录当前请求
        self.requests[key].append(now)
        return False
    
    def cleanup_expired_records(self):
        """清理过期的请求记录"""
        now = time.time()
        expired_keys = []
        
        for key, requests in self.requests.items():
            # 移除5分钟前的记录
            self.requests[key] = [
                req_time for req_time in requests 
                if now - req_time < 300
            ]
            
            # 标记空记录的键
            if not self.requests[key]:
                expired_keys.append(key)
        
        # 删除空记录
        for key in expired_keys:
            del self.requests[key]


class ErrorHandlingMiddleware:
    """错误处理中间件"""
    
    def __init__(self, app=None):
        self.app = app
        if app is not None:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化错误处理中间件"""
        app.register_error_handler(ValidationError, self.handle_validation_error)
        app.register_error_handler(404, self.handle_not_found)
        app.register_error_handler(405, self.handle_method_not_allowed)
        app.register_error_handler(500, self.handle_internal_error)
        app.register_error_handler(Exception, self.handle_generic_error)
    
    def handle_validation_error(self, e):
        """处理Pydantic验证错误"""
        return jsonify({
            'success': False,
            'message': '数据验证失败',
            'errors': ValidationMiddleware.format_validation_errors(e.errors()),
            'timestamp': datetime.now().isoformat()
        }), 400
    
    def handle_not_found(self, e):
        """处理404错误"""
        return jsonify({
            'success': False,
            'message': '请求的资源不存在',
            'path': request.path,
            'timestamp': datetime.now().isoformat()
        }), 404
    
    def handle_method_not_allowed(self, e):
        """处理405错误"""
        return jsonify({
            'success': False,
            'message': f'方法 {request.method} 不被允许',
            'allowed_methods': list(e.valid_methods) if hasattr(e, 'valid_methods') else [],
            'timestamp': datetime.now().isoformat()
        }), 405
    
    def handle_internal_error(self, e):
        """处理500错误"""
        logging.error(f"内部服务器错误: {str(e)}")
        
        # 在开发环境显示详细错误信息
        if current_app.debug:
            return jsonify({
                'success': False,
                'message': '内部服务器错误',
                'error_detail': str(e),
                'timestamp': datetime.now().isoformat()
            }), 500
        else:
            return jsonify({
                'success': False,
                'message': '服务器内部错误，请稍后重试',
                'timestamp': datetime.now().isoformat()
            }), 500
    
    def handle_generic_error(self, e):
        """处理通用异常"""
        logging.error(f"未处理的异常: {str(e)}")
        
        return jsonify({
            'success': False,
            'message': '服务器处理请求时发生错误',
            'timestamp': datetime.now().isoformat()
        }), 500


# 使用示例
def setup_middlewares(app):
    """设置所有中间件"""
    # 请求日志
    RequestLoggingMiddleware(app)
    
    # 错误处理
    ErrorHandlingMiddleware(app)
    
    # 认证中间件
    auth_middleware = AuthenticationMiddleware(app)
    
    # 限流中间件
    rate_limit_middleware = RateLimitMiddleware(app)
    
    return {
        'auth': auth_middleware,
        'rate_limit': rate_limit_middleware
    }